package com.niiwoo.civet.trade.service.local.fund;

import com.niiwoo.civet.base.enums.FeeTypeEnum;
import com.niiwoo.civet.trade.constant.DatePatternConstant;
import com.niiwoo.civet.trade.constant.RabbitConstant;
import com.niiwoo.civet.trade.dao.entity.ProjectBorrowerPlanDetail;
import com.niiwoo.civet.trade.dao.entity.ProjectBorrowerPlanNew;
import com.niiwoo.civet.trade.dao.entity.ProjectBorrowerSummary;
import com.niiwoo.civet.trade.dao.entity.ProjectCreditorFund;
import com.niiwoo.civet.trade.dao.mapper.ProjectBorrowerPlanDetailMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectBorrowerPlanNewMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectBorrowerSummaryMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectCreditorFundMapperExt;
import com.niiwoo.civet.trade.dto.RefundListByUserDTO;
import com.niiwoo.civet.trade.dto.common.TradeBatchTransOrderDTO;
import com.niiwoo.civet.trade.enums.RepayStatusEnum;
import com.niiwoo.civet.trade.service.local.refund.CommonLocalService;
import com.niiwoo.civet.trade.service.local.refund.TransDetailLocalService;
import com.niiwoo.civet.trade.utils.RepaymentUtil;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.FastDateFormat;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import static com.niiwoo.civet.trade.enums.RepayStatusEnum.BADLOANS;
import static com.niiwoo.civet.trade.enums.RepayStatusEnum.OVERDUE;

/**
 * 资金端还款公共服务
 *
 * @author lichuanzhi
 * @date 2018/01/25
 */

@Slf4j
@Service
public class FundRepaymentService {
    @Autowired
    private ProjectBorrowerSummaryMapperExt projectBorrowerSummaryMapper;
    @Autowired
    private ProjectCreditorFundMapperExt projectCreditorFundMapper;
    @Autowired
    private ProjectBorrowerPlanNewMapperExt projectBorrowerPlanMapper;
    @Autowired
    private ProjectBorrowerPlanDetailMapperExt borrowerPlanDetailMapperExt;

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private CommonLocalService commonLocalService;
    @Autowired
    private TransDetailLocalService transDetailLocalService;
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;

    /**
     * 定时自动更新罚息和标的状态
     */
    public void sendWaitingUpdateUserid() {
        List<ProjectBorrowerSummary> summaries = projectBorrowerSummaryMapper.refundByUsers4Fund();
        int i = 0;
        for (ProjectBorrowerSummary summary : summaries) {
            try {
                boolean isCurrentDate = false;
                if (Objects.nonNull(summary.getPenaltyUpdateDate())) {
                    DateTime penaltyUpdateDate = new DateTime(FastDateFormat.getInstance(DatePatternConstant.YYYY_MM_DD).format(summary.getPenaltyUpdateDate()));
                    DateTime currentDate = new DateTime(FastDateFormat.getInstance(DatePatternConstant.YYYY_MM_DD).format(new Date()));
                    isCurrentDate = Days.daysBetween(currentDate, penaltyUpdateDate).getDays() < 0;
                }
                if (isCurrentDate || Objects.isNull(summary.getPenaltyUpdateDate())) {
                    rabbitTemplate.convertAndSend(RabbitConstant.Exchange.FUND_REPAY, RabbitConstant.RoutingKey.FUND_REPAY_UPDATE_PENALTY, summary.getBorrowerUserId());
                    i++;
                }
            } catch (Exception e) {
                log.error("资金端**还款人发送异常",e);
            }
        }
        log.info("资金端**还款人列表发送完毕,用户ID数:{}",i);
    }


    public void autoUpdatePenaltyAndProjectStatus(String userId) {
        RefundListByUserDTO dto = projectBorrowerSummaryMapper.refundListByUsers4Fund(userId);
        if (dto == null || CollectionUtils.isEmpty(dto.getBorrowerSummarys())) {
            throw new BizException("TRD21004",userId);
        }
        TradeBatchTransOrderDTO dtoKaola = null;
        for (ProjectBorrowerSummary summary : dto.getBorrowerSummarys()){
            dtoKaola = new TradeBatchTransOrderDTO();
            dtoKaola.setProjectId(summary.getProjectId());
            dtoKaola.setBorrowerUserId(summary.getBorrowerUserId());
            dtoKaola.setCurrentPeriod(summary.getCurrentPeriod());
            dtoKaola.setRefundDate(summary.getRefundDate());
            transDetailLocalService.handleProjectStatus4Fund(dtoKaola, new DateTime(summary.getRefundDate()));
        }
    }

    public void handPlanPenalty(ProjectBorrowerSummary summary) {
        List<ProjectBorrowerPlanNew> plans = projectBorrowerPlanMapper.selectNeedRefundList(summary.getProjectId(), null);
        Date updateToday = commonLocalService.getKaolaCurrentDate();

        //罚息计算截止日,下一期还款日和当前时间，取较小值，只有一期或最后一期的情况，这里默认是日切日期（这种情况坏账也是直接按当前时间直接算逾期天数即可，因为坏账本息和逾期本息是一样的）
        Date penaltyEndDate = plans.stream()
                .filter(plan -> plan.getPeriod().equals(summary.getCurrentPeriod() + 1))
                .findFirst()
                .map(plan -> new Date(Math.min(plan.getRefundDate().getTime(), updateToday.getTime())))
                .orElse(updateToday);

        for (ProjectBorrowerPlanNew plan : plans) {
            RepayStatusEnum repayStatus = RepayStatusEnum.enumOf(summary.getRepayStatus());

            /*
             坏账逾期的罚息还是要算，算在逾期的那一期，比如共3期，当前期为第1期,则进入到第3期坏账，除了需要计算逾期罚息外下面还要计算真正的坏账罚息
             ，计算方式还是以逾期的方式计算.计算的罚息更新到当前期
             */
            boolean isUpdateOverduePenalty = (repayStatus == OVERDUE || repayStatus == BADLOANS) && plan.getPeriod().equals(summary.getCurrentPeriod());
            if (isUpdateOverduePenalty) {
                updatePenalty(summary, updateToday, penaltyEndDate, plan, false);
            } else if (repayStatus == BADLOANS && plan.getPeriod().equals(summary.getCurrentPeriod() + 1)) {

                //如果是坏账，更新当前期的下一期还款计划的罚息，将罚息更新到当前期的下一期. 坏账罚息截止日取日切日期
                updatePenalty(summary, updateToday, updateToday, plan, true);
            }
        }
    }

    private void updatePenalty(ProjectBorrowerSummary summary, Date updateToday, Date penaltyEndDate, ProjectBorrowerPlanNew plan, boolean updateBadLoans) {
        BigDecimal penalty = this.getTotalPenalty(summary, plan.getPeriod(), penaltyEndDate, updateBadLoans);
        log.info("资金端 handPlanPenalty for {} ,projectId:{} ,penalty:{},period:{},逾期截止日:{}", updateBadLoans ? "BadLoans" : "Overdue", plan.getProjectId(), penalty, plan.getPeriod(), penaltyEndDate);
        ProjectBorrowerPlanDetail planDetail = borrowerPlanDetailMapperExt.queryProjectBorrowerPlanDetailByPlanIdAndFeeType(plan.getId(), FeeTypeEnum.PENALTY.getValue());
        if (penalty.compareTo(BigDecimal.ZERO) > 0) {
            insertOrUpdatePenalty(plan, penalty, planDetail);
            ProjectBorrowerSummary borrowerSummary = new ProjectBorrowerSummary();
            borrowerSummary.setProjectId(summary.getProjectId());
            borrowerSummary.setPenaltyUpdateDate(updateToday);
            projectBorrowerSummaryMapper.updateByPrimaryKeySelective(borrowerSummary);
        }

    }

    private void insertOrUpdatePenalty(ProjectBorrowerPlanNew plan, BigDecimal penalty, ProjectBorrowerPlanDetail planDetail) {
        if (planDetail == null) {
            ProjectBorrowerPlanDetail initPenaltyDetail = new ProjectBorrowerPlanDetail();
            initPenaltyDetail.setId(snowflakeIdWorker.nextId());
            initPenaltyDetail.setMoney(penalty);
            initPenaltyDetail.setUpdateTime(new Date());
            initPenaltyDetail.setCreateTime(new Date());
            initPenaltyDetail.setFeeType(FeeTypeEnum.PENALTY.getValue());
            initPenaltyDetail.setPlanId(plan.getId());
            initPenaltyDetail.setRefundMoney(BigDecimal.ZERO);
            initPenaltyDetail.setProjectId(plan.getProjectId());
            initPenaltyDetail.setUserId(plan.getUserId());
            borrowerPlanDetailMapperExt.insertSelective(initPenaltyDetail);
        } else {
            ProjectBorrowerPlanDetail planDetailUpdater = new ProjectBorrowerPlanDetail();
            planDetailUpdater.setId(planDetail.getId());
            planDetailUpdater.setMoney(penalty.add(planDetail.getRefundMoney()));
            planDetailUpdater.setUpdateTime(new Date());
            borrowerPlanDetailMapperExt.updateByPrimaryKeySelective(planDetailUpdater);
        }
    }


    public BigDecimal getTotalPenalty(ProjectBorrowerSummary summary,Integer currentPeriod, Date penaltyEndDate, Boolean isBadLoans) {
        List<ProjectCreditorFund> creditorFunds = projectCreditorFundMapper.listWaitingSettleCreditor(summary.getProjectId());
        if (CollectionUtils.isEmpty(creditorFunds)) {
            throw new BizException("TRD21005",summary.getProjectId());
        }

        BigDecimal baseAmount=BigDecimal.ZERO;
        BigDecimal orignalPenaltyFee = null;

        /*罚息开始日，老数据同步过来的时候
         1.正常还款中的，所有代还款期的罚息计算开始日为那一期的还款日
         2.逾期还款中的，将老库中剩余罚息放到当前期的初始罚息，逾期那一期的罚息计算开始日为迁移当天，其他待还期的初始罚息为零，罚息计算开始日为那一期的还款日
         3.严重逾期的，将老库中剩余罚息都放到当前期的初始罚息，逾期那一期及坏账期的罚息计算开始日为迁移当天（因为逾期天数为负数所以只会用到初始罚息，逾期的罚息不再需要，后面只需累加每天的坏账罚息即可）
         ，其他待还期的初始罚息为0，其他罚息计算开始日为那一期的还款日
         以后新增的数据，则默认为当期还款日
         */
        Date penaltyBegainDate = null;

        BigDecimal penaltyRate = summary.getPenaltyRate();
        for (ProjectCreditorFund fundCreditor : creditorFunds){
            if (isBadLoans) {

                /*
                如果是坏账，此时的CurrentPeriod = CurrentPeriod+1，所以 -1 刚好就是summary中的当前期
                坏账时计算基础金额baseAmount = 逾期当期应还利息+坏账当期应还利息+全部期数未还本金
                */
                baseAmount = baseAmount.add(fundCreditor.getCapital());
                if (currentPeriod > 1 && (fundCreditor.getCurrentPeriod()) == (currentPeriod - 1)) {

                    //坏账时，逾期当期利息
                    baseAmount = baseAmount.add(fundCreditor.getInterest());

                    //初始罚息，初始化为0
                    orignalPenaltyFee =  BigDecimal.ZERO;

                    //默认坏账期开始日为当期还款日+1个月
                    penaltyBegainDate = new DateTime(fundCreditor.getExpiryDate()).plusMonths(1).toDate();
                }
                if ((fundCreditor.getCurrentPeriod()) == currentPeriod.intValue()) {

                    //坏账时，坏账当期利息
                    orignalPenaltyFee = fundCreditor.getOrignalPenaltyFee();

                    //默认罚息开始计时日为还款日，所以这里就是summary中当前期下一期的还款日即从坏账那一期开始算
                    penaltyBegainDate = fundCreditor.getPenaltyBeginDate();
                    baseAmount = baseAmount.add(fundCreditor.getInterest());
                }
            }

            //逾期
            else if (fundCreditor.getCurrentPeriod().equals(currentPeriod)) {
                baseAmount = fundCreditor.getCapital().add(fundCreditor.getInterest());
                orignalPenaltyFee = fundCreditor.getOrignalPenaltyFee();

                //如果同步过来的时候已经坏账则逾期天数是负数，在下面计算罚息RepaymentUtil.getOverdueDay的时候置为了0
                // （相当于只用了逾期这一期的初始罚息）
                penaltyBegainDate = fundCreditor.getPenaltyBeginDate();
                break;
            }
        }
        if(penaltyBegainDate == null){
            log.error("penaltyBegainDate为空，没有罚息projectId{},currentPeriod{}",summary.getProjectId(),currentPeriod);
            return BigDecimal.ZERO;
        }

        //第二个参数日期 - 第一个参数日期
        int overdueDays = RepaymentUtil.getOverdueDay(penaltyBegainDate,penaltyEndDate);

        //初始罚息和罚息开始计算日期会在每次还款时变动，罚息
        BigDecimal penaltyFeeTotal = this.getCreditorPenaltyFeeDays(orignalPenaltyFee,baseAmount,penaltyRate,overdueDays);
        log.info("标的：{}，{}，penaltyFeeTotal={},orignalPenaltyFee={},overdueDays={},baseAmount={}",summary.getProjectId(),
                isBadLoans ? "坏账罚息" : "逾期罚息",penaltyFeeTotal,orignalPenaltyFee,overdueDays,baseAmount);
        return penaltyFeeTotal;
    }

    /**
     *
     * @param
     * @param baseAmount
     * @param penaltyRate
     * @param overdueDays
     * @return
     */
    public BigDecimal getCreditorPenaltyFeeDays(BigDecimal originPenaltyFee,BigDecimal baseAmount,BigDecimal penaltyRate,Integer overdueDays){
        if (overdueDays<0){
            log.info("逾期天数overdueDays不可小于0,baseAmount={}", baseAmount);
            overdueDays = 0;
        }
        //初始罚息+按天计算罚息-已收罚息
        BigDecimal penaltyFeeOne = originPenaltyFee.add( baseAmount.multiply(penaltyRate).multiply(new BigDecimal(overdueDays)))
                .setScale(2,BigDecimal.ROUND_HALF_EVEN);
        if (penaltyFeeOne.compareTo(BigDecimal.ZERO) < 0){
            log.info("罚息小于0,baseAmount{},overdueDays{}",baseAmount,overdueDays);
            penaltyFeeOne = BigDecimal.ZERO;
        }
        return penaltyFeeOne;
    }


}
